package com.jin.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.jin.common.exception.NoStockException;
import com.jin.common.to.OrderTo;
import com.jin.common.utils.R;
import com.jin.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.jin.gulimall.ware.entity.WareOrderTaskEntity;
import com.jin.gulimall.ware.feign.OrderFeignService;
import com.jin.gulimall.ware.feign.ProductFeignService;
import com.jin.gulimall.ware.service.WareOrderTaskDetailService;
import com.jin.gulimall.ware.service.WareOrderTaskService;
import com.jin.gulimall.ware.to.StockLockedTo;
import com.jin.gulimall.ware.vo.*;
import lombok.Data;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jin.common.utils.PageUtils;
import com.jin.common.utils.Query;

import com.jin.gulimall.ware.dao.WareSkuDao;
import com.jin.gulimall.ware.entity.WareSkuEntity;
import com.jin.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    WareSkuDao wareSkuDao;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareOrderTaskDetailService orderTaskDetailService;

    @Autowired
    OrderFeignService orderFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;


    /**
     * 解锁库存具体实现方法
     * @param skuId
     * @param wareId
     * @param skuNum
     * @param detailId
     */
    private void unlockedStock(Long skuId, Long wareId, Integer skuNum, Long detailId) {
        //解锁
        wareSkuDao.unlockedStock(skuId, wareId, skuNum);
        //更新工作单详情中的锁定状态为1
        //要根据detailId来更新，因为只有id是不变的
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(detailId);
        taskDetailEntity.setLockStatus(2);//已解锁
        orderTaskDetailService.updateById(taskDetailEntity);
    }



    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        /**
         * skuId: 2
         * wareId: 1
         */
        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId) && !"0".equalsIgnoreCase(skuId)) {
            queryWrapper.eq("sku_id", skuId);
        }
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId) && !"0".equalsIgnoreCase(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Transactional
    @Override
    public void addStock(Long skuId, Integer skuNum, Long wareId) {
        //判断如果没有这个库存记录则进行新增操作
        List<WareSkuEntity> list = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (list == null || list.size() == 0) {
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setWareId(wareId);
            skuEntity.setStock(skuNum);
            skuEntity.setStockLocked(0);
            //使用catch来捕捉异常，因为当远程调用不稳定等原因有异常，、
            //一旦抛出异常就会导致事务回滚（因为名字插入不上回滚不划算）
            //所以用catch捕捉后就不会抛异常了，从而也不会因此回滚了
            //高级篇还有其他的解决方案
            try {
                R info = productFeignService.info(skuId);
                if (info.getCode() == 0) {
                    Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                    skuEntity.setSkuName((String) skuInfo.get("skuName"));
                }
            } catch (Exception e) {

            }
            wareSkuDao.insert(skuEntity);
        } else {
            wareSkuDao.addStock(skuId, skuNum, wareId);
        }
    }

    @Override
    public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
        List<SkuHasStockVo> skuHasStockVos = skuIds.stream().map(skuId -> {
            SkuHasStockVo vo = new SkuHasStockVo();
            //查询当前sku的总库存量
            Long count = this.baseMapper.getSkuStock(skuId);
            vo.setSkuId(skuId);
            vo.setHasStock(count != null && count > 0);
            return vo;
        }).collect(Collectors.toList());
        return skuHasStockVos;
    }

    /**
     * 锁定库存
     *
     * @param vo
     * @return
     */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {
        //保存工作单详情
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskEntity);

        //按照下单的收货地址，找到一个就近仓库,锁定库存
        //1、找到每个商品在哪个仓库都有库存
        List<OrderItemVo> locks = vo.getLocks();
        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock hasStock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            hasStock.setSkuId(skuId);
            hasStock.setNum(item.getCount());
            //查询这个商品在哪里有库存
            List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
            hasStock.setWareIds(wareIds);
            return hasStock;
        }).collect(Collectors.toList());
        //2.锁定库存
        for (SkuWareHasStock stock : collect) {
            Long skuId = stock.getSkuId();
            List<Long> wareIds = stock.getWareIds();
            Integer num = stock.getNum();
            if (wareIds == null || wareIds.size() == 0) {
                //没有任何仓库有这个商品的库存
                throw new NoStockException(skuId.toString());
            }
            //设置是否有库存的标识
            Boolean skuStock = false;
            for (Long wareId : wareIds) {
                //返回1，锁定成功；返回0，锁定失败
                Long count = wareSkuDao.lockSkuStock(skuId, wareId, num);
                if(count>0){
                    //TODO: 锁定成功
                    skuStock = true;
                    //TODO: 告诉MQ库存锁定成功
                    WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity(null, stock.skuId, null, stock.num, taskEntity.getId(),wareId,1);
                    orderTaskDetailService.save(detailEntity);
                    StockLockedTo lockedTo = new StockLockedTo();
                    lockedTo.setId(taskEntity.getId());
                    lockedTo.setDetail(detailEntity);
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",lockedTo);
                    break;
                }
            }
            if(!skuStock){
                //没有库存
                throw new NoStockException(skuId.toString());
            }
        }
        //3.到这一步一定是所有库存都锁定成功了
        return true;
    }

    /**
     * 解锁库存
     * @param to
     */
    @Override
    public void unLockStocked(StockLockedTo to) {
        WareOrderTaskDetailEntity detail = to.getDetail();
        Long detailId = detail.getId();
        //获取工作单详情实体类，往后要用
        WareOrderTaskDetailEntity detailEntity = orderTaskDetailService.getById(detailId);
        if(detailEntity!=null){
            Long id = to.getId();//库存工作单id
            WareOrderTaskEntity entity = wareOrderTaskService.getById(id);
            String orderSn = entity.getOrderSn();
            R r = orderFeignService.OrderByOrderSn(orderSn);
            if(r.getCode()==0){
                //没有订单data==null，或订单状态已取消需要解锁
                OrderVo data = r.getData(new TypeReference<OrderVo>() {
                });
                if(data==null||data.getStatus()==4){
                    //解锁
                    //如果工作单详情中状态为1，已锁定才可以去解锁
                    if(detailEntity.getLockStatus()==1){
                        unlockedStock(detailEntity.getSkuId(), detailEntity.getWareId(), detailEntity.getSkuNum(),detailId);
                    }
                }else{
                    //订单存在其状态为其他状态，无需解锁
                }
            }else{
                //解锁失败，将消息重新传入到队列中等待其他消费者消费
                throw new RuntimeException("远程服务失败");
            }
        }else{
            //无需解锁(订单实体类都没有，说明库存锁定没有成功所以无需解锁)
        }
    }

    /**
     * 订单发消息后解锁
     * 因为如果订单因为网络等原因延时修改状态，库存一看状态是新建就不再加锁，以后订单修改成功库存也不会改了
     * @param to
     */
    @Transactional
    @Override
    public void unLockStocked(OrderTo to) {
        String orderSn = to.getOrderSn();
        WareOrderTaskEntity entity=wareOrderTaskService.getTaskByOrderSn(orderSn);
        Long id = entity.getId();
        List<WareOrderTaskDetailEntity> entities = orderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", id)
                .eq("lock_status", 1));
        for (WareOrderTaskDetailEntity detailEntity : entities) {
            //Long skuId, Long wareId, Integer skuNum, Long detailId
            unlockedStock(detailEntity.getSkuId(),detailEntity.getWareId(),detailEntity.getSkuNum(),detailEntity.getId());
        }
    }

    @Data
    class SkuWareHasStock {
        private Long skuId;
        private Integer num;
        private List<Long> wareIds;
    }
}